home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume91 / utilitys / difdir20 / part01
Encoding:
Text File  |  1991-02-18  |  56.4 KB  |  1,875 lines

  1. Path: news.larc.nasa.gov!amiga-request
  2. From: amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator)
  3. Subject: v91i013: DiffDir 2.0 - directory comparison utility, Part01/01
  4. Reply-To: mrr@mrsoft.Newport.RI.US (Mark Rinfret)
  5. Newsgroups: comp.sources.amiga
  6. Message-ID: <comp.sources.amiga:v91i013@ab20.larc.nasa.gov>
  7. Date: 18 Feb 91 20:58:42 GMT
  8. Approved: tadguy@uunet.UU.NET (Tad Guy)
  9. X-Mail-Submissions-To: amiga@uunet.uu.net
  10. X-Post-Discussions-To: comp.sys.amiga.misc
  11.  
  12. Submitted-by: mrr@mrsoft.Newport.RI.US (Mark Rinfret)
  13. Posting-number: Volume 91, Issue 013
  14. Archive-name: utilities/diffdir-2.0/part01
  15.  
  16. This is DiffDir Version 2.0, an AmigaDOS directory comparison utility.
  17. DiffDir will scan two directories and generate a report describing all
  18. differences encountered.  In addition, DiffDir will optionally do a
  19. binary file compare on files which otherwise seem to be the same.  A
  20. user-customizable file comparison script can optionally be generated,
  21. inserting the user's favorite file-comparison command.  Source,
  22. documentation, and a two test directory hierarchies are included.
  23.  
  24.  
  25.  
  26. #!/bin/sh
  27. # This is a shell archive.  Remove anything before this line, then unpack
  28. # it by saving it into a file and typing "sh file".  To overwrite existing
  29. # files, type "sh file -c".  You can also feed this as standard input via
  30. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  31. # will see the following message at the end:
  32. #        "End of archive 1 (of 1)."
  33. # Contents:  DiffDir.c MRDates.c MRDates.h dir1 dir1/File1 dir1/File3
  34. #   dir1/Level2 dir1/Level2/File1 dir1/Level2/File3 dir1/Level2/Level3
  35. #   dir1/Level2/Level3/File1 dir1/Level2/Level3/File2
  36. #   dir1/Level2/Level3/File3 dir1/file2 dir2 dir2/DifferentType
  37. #   dir2/File1 dir2/File2 dir2/Level2 dir2/Level2/File1
  38. #   dir2/Level2/File2 dir2/Level2/File3 dir2/Level2/Level3
  39. #   dir2/Level2/Level3/File2 dir2/Level2/Level3/File3 dir2/file3
  40. #   makefile
  41. # Wrapped by tadguy@ab20 on Mon Feb 18 15:58:36 1991
  42. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  43. if test -f 'DiffDir.c' -a "${1}" != "-c" ; then 
  44.   echo shar: Will not clobber existing file \"'DiffDir.c'\"
  45. else
  46. echo shar: Extracting \"'DiffDir.c'\" \(27265 characters\)
  47. sed "s/^X//" >'DiffDir.c' <<'END_OF_FILE'
  48. X/*  DiffDir - Compare directories for differences.
  49. X    Filename:   DiffDir.c
  50. X
  51. X    (C)Copyright 1988-1991 by Mark R. Rinfret, All Rights Reserved.
  52. X    This software may be freely distributed for non-profit use only.
  53. X    You are free to make changes and redistribute this program as
  54. X    long as the source is distributed and this notice is kept intact.
  55. X
  56. X    History (most recent change first):
  57. X
  58. X    02/15/91 V2.0 (MRR)
  59. X        Oh, what the hey! Let's really change things! There are lots of
  60. X        internal as well as external differences. Read the docs :-).
  61. X
  62. X    02/09/91 V1.2 (MRR)
  63. X        Added -b flag (binary file compare)
  64. X
  65. X    07/04/89 V1.1 (MRR)
  66. X        Added flags for specific tests.
  67. X
  68. X    12/31/88 V1.0 (MRR)
  69. X        Program conception and implementation.
  70. X
  71. X    I wrote DiffDir to assist me with configuration management.  Though
  72. X    I keep all of my PD files on floppy disk, I usually roll them onto
  73. X    the hard disk when I want to make changes.  Sometimes, I forget to
  74. X    copy the hard disk version back to floppy or I forget that I've already
  75. X    done it.  DiffDir scans two directories and reports the following
  76. X    discrepancies:
  77. X
  78. X        1. File dates are different.
  79. X        2. File protection flags are different.
  80. X        3. File names are not exact (case discrepancy).
  81. X        4. File sizes are different.
  82. X        5. File comments are different.
  83. X        6. File exists in one directory but not the other.
  84. X        7. File contents differ.
  85. X
  86. X    See the Usage() function for command line options.
  87. X
  88. X*/
  89. X
  90. X#include <stdio.h>
  91. X#include <stdlib.h>
  92. X#include <string.h>
  93. X#include <ctype.h>
  94. X#include <fcntl.h>
  95. X#include <exec/types.h>
  96. X#include <exec/memory.h>
  97. X#include <libraries/dos.h>
  98. X#include <functions.h>
  99. X#include "MRDates.h"
  100. X
  101. Xchar    *version = "DiffDir 2.0, Mark R. Rinfret, 02/15/91\n";
  102. X
  103. Xtypedef struct fileList {
  104. X    USHORT          fileCount;
  105. X    char            *dName;     /* directory name for this list */
  106. X    struct fileNode *firstEntry, *lastEntry;
  107. X    } FileList;
  108. X
  109. Xtypedef struct fileNode {
  110. X    struct fileNode *next, *prev;
  111. X    struct fileList *parentList;    /* the list that I belong to */
  112. X    char            *name;
  113. X    LONG            flags;          /* protection, other bits */
  114. X    char            *comment;       /* NULL if comment was empty */
  115. X    struct DateStamp date;
  116. X    ULONG           size;           /* in bytes */
  117. X    BOOL            isDir;          /* TRUE => node is a directory */
  118. X    struct FileNode *subList;       /* sublist for directory node */
  119. X    } FileNode;
  120. X
  121. X#define NAMES_DONT_MATCH    1
  122. X#define DATES_DONT_MATCH    2
  123. X#define FLAGS_DONT_MATCH    4
  124. X#define SIZES_DONT_MATCH    8
  125. X#define COMMENTS_DONT_MATCH 16
  126. X
  127. X#define ITEM_COUNT          5   /* Make sure this tracks the list above! */
  128. X
  129. Xstatic char *errorDesc[ITEM_COUNT] = {
  130. X        " names ", " dates ", " flags ", " sizes ", " comments " };
  131. X
  132. X/* The scriptCmd is inserted into the command string output to the optional
  133. X * comparison script. It may be over-ridden by the user. 
  134. X */
  135. Xchar        scriptCmd[128] = "cmp";
  136. X
  137. Xvoid                    AddNode(FileNode *node, FileList *list);
  138. Xint                     CollectFiles(FileList *list);
  139. Xint                     CompareDirs(FileList *list1, FileList *list2);
  140. Xvoid                    CompareFileContents(FileNode *f1, FileNode *f2);
  141. Xint                     CompareFiles(FileList *l1, FileList *l2);
  142. Xint                     CompareLists(FileList *l1, FileList *l2);
  143. Xchar                    *DupString(const char *oldString);
  144. XFileNode                *FindFile(FileNode *node, FileList *list);
  145. Xvoid                    FreeNode(FileNode *node, FileList *list);
  146. Xchar                    *MakeDirName(const char *s1, const char *s2);
  147. Xvoid                    *MyAlloc(size_t size);
  148. Xvoid                    MyExit(int code);
  149. Xvoid                    MyFree(void *ptr);
  150. Xint                     OpenFile(FileNode *f);
  151. Xvoid                    ReportStats(void);
  152. Xint                     stricmp(const char *s1, const char *s2);
  153. Xvoid                    Usage(void);
  154. Xvoid                    WriteFileInfo(FileNode *node);
  155. X
  156. X
  157. Xint                     checkContents = TRUE;
  158. Xstruct FileInfoBlock    *fib;
  159. XBOOL                    ignoreCase = FALSE;
  160. XUSHORT                  level = 0;
  161. XFileList                list1, list2;
  162. XLONG                    maxMemUsed, memInUse;
  163. XBOOL                    outputScript = FALSE;
  164. XFILE                    *scriptFile;
  165. XULONG                   testFlags = 0;
  166. XLONG                    totalFiles, totalDirs;
  167. XBOOL                    verbose = FALSE;
  168. X
  169. Xmain(argc, argv)
  170. X    int argc; char **argv;
  171. X{
  172. X    int     leng;
  173. X    char    *ptr;
  174. X
  175. X    testFlags = 0xFFFFFFFF;
  176. X
  177. X    while (--argc > 0 && **++argv == '-') {
  178. X        ptr = &argv[0][1];
  179. X        leng = strlen(ptr);
  180. X        if (leng < 5) {
  181. X            Usage();
  182. X        }
  183. X        if (strncmp("nocontents", ptr, leng) == 0) {      
  184. X                /* Compare file contents. */
  185. X                checkContents = FALSE;
  186. X        } else if (strncmp(ptr, "nocomments", leng) == 0) {
  187. X                /* Ignore comments. */
  188. X                testFlags &= (~COMMENTS_DONT_MATCH);
  189. X        } else if (strncmp(ptr, "nodates", leng) == 0) {  
  190. X                /* Suppress date test. */
  191. X                testFlags &= (~DATES_DONT_MATCH);
  192. X        } else if (strncmp(ptr, "noflags", leng) == 0) {
  193. X                /* Ignore flags (protection word). */
  194. X                testFlags &= (~FLAGS_DONT_MATCH);
  195. X        } else if (strncmp(ptr, "nosizes", leng) == 0) {
  196. X            testFlags &= (~SIZES_DONT_MATCH);
  197. X        } else if (strncmp(ptr, "nocase", leng) == 0) {
  198. X                /* Ignore filename case. */
  199. X                ignoreCase = TRUE;
  200. X        } else if (strncmp(ptr, "script", leng) == 0) {
  201. X            if (--argc) {
  202. X                ++argv;
  203. X                scriptFile = fopen(*argv, "w");
  204. X                if (!scriptFile) {
  205. X                    perror("Script file would not open!");
  206. X                    exit(20);
  207. X                }
  208. X            }
  209. X        } else if (strncmp(ptr, "command", leng) == 0) {
  210. X            if (--argc) {
  211. X                ++argv;
  212. X                strcpy(scriptCmd, *argv);
  213. X            }
  214. X            else 
  215. X                Usage();
  216. X        } else if (strncmp(ptr, "verbose", leng) == 0) {
  217. X                verbose = TRUE;
  218. X        } else {
  219. X                Usage();
  220. X        }
  221. X    }
  222. X    if (argc != 2) Usage();
  223. X    list1.dName = MakeDirName("",*argv++);
  224. X    list2.dName = MakeDirName("",*argv);
  225. X    fib = MyAlloc(sizeof(*fib));
  226. X    if (fib == NULL) {
  227. X        printf("DiffDir: unable to allocate file info block!\n");
  228. X        goto done;
  229. X    }
  230. X
  231. X    if (! CollectFiles(&list1))
  232. X        if (! CollectFiles(&list2))
  233. X            CompareLists(&list1, &list2);
  234. Xdone:
  235. X    if (fib)     MyFree(fib);
  236. X    if (verbose) ReportStats();
  237. X}
  238. X
  239. X/*  FUNCTION
  240. X        AddNode - add file info node to list.
  241. X
  242. X    SYNOPSIS
  243. X        AddNode(FileNode *node, FileList *list);
  244. X
  245. X    DESCRIPTION
  246. X        AddNode adds the <node> to the <list>.  Right now, a very lazy
  247. X        approach is taken (adds to end of list).  Perhaps later, we'll
  248. X        make the list a binary tree or better.
  249. X
  250. X*/
  251. X
  252. Xvoid
  253. XAddNode(FileNode *node, FileList *list)
  254. X{
  255. X    if (list->firstEntry) {         /* List has stuff in it? */
  256. X        list->lastEntry->next = node;
  257. X    }
  258. X    else {
  259. X        list->firstEntry = node;    /* This is the first entry. */
  260. X    }
  261. X    node->prev = list->lastEntry;
  262. X    list->lastEntry = node;
  263. X    ++list->fileCount;
  264. X    if (node->isDir)
  265. X        ++totalDirs;
  266. X    else
  267. X        ++totalFiles;
  268. X}
  269. X
  270. X/*  FUNCTION
  271. X        CollectFiles - collect files for one directory level.
  272. X
  273. X    SYNOPSIS
  274. X        int CollectFiles(FileList *list);
  275. X
  276. X    DESCRIPTION
  277. X        CollectFiles scans the directory pointed to by <list> and creates
  278. X        list entry nodes for each file or directory found.  A zero is
  279. X        returned on success, non-zero otherwise.
  280. X*/
  281. X
  282. Xint
  283. XCollectFiles(FileList *list)
  284. X{
  285. X    int         errCode;
  286. X    BPTR        lock = 0;
  287. X    FileNode    *fNode = NULL;
  288. X    int         result = 0;
  289. X
  290. X    if (verbose)
  291. X        printf("DiffDir: scanning '%s'\n", list->dName);
  292. X
  293. X    lock = Lock(list->dName, SHARED_LOCK);
  294. X    if (lock == 0) {
  295. X        result = IoErr();
  296. X        printf("DiffDir: failed to lock '%s'!\n", list->dName);
  297. X        goto done;
  298. X    }
  299. X    if (Examine(lock, fib) == 0) {
  300. X        result = IoErr();
  301. X        printf("DiffDir: failed to get info for '%s'!\n", list->dName);
  302. X        goto done;
  303. X    }
  304. X
  305. X    if (fib->fib_DirEntryType < 0) {
  306. X        result = -1;
  307. X        printf("DiffDir: '%s' is not a directory!\n", list->dName);
  308. X        goto done;
  309. X    }
  310. X
  311. X    while (!result && ExNext(lock, fib)) {
  312. X        fNode = MyAlloc(sizeof(FileNode));
  313. X        fNode->parentList = list;
  314. X        fNode->name = DupString(fib->fib_FileName);
  315. X        fNode->isDir = (fib->fib_DirEntryType > 0);
  316. X        fNode->flags = fib->fib_Protection;
  317. X        if (*fib->fib_Comment)
  318. X            fNode->comment = DupString(fib->fib_Comment);
  319. X        fNode->date = fib->fib_Date;
  320. X        fNode->size = fib->fib_Size;
  321. X        AddNode(fNode, list);
  322. X    }
  323. X    errCode = IoErr();
  324. X    if (errCode != ERROR_NO_MORE_ENTRIES) {
  325. X        result = errCode;
  326. X        printf("DiffDir: scan of directory '%s' failed!\n", list->dName);
  327. X    }
  328. Xdone:
  329. X    if (lock) UnLock(lock);
  330. X    return result;
  331. X}
  332. X
  333. X/*  FUNCTION
  334. X        CompareLists - compare files and directories in two lists.
  335. X
  336. X    SYNOPSIS
  337. X        int CompareLists(FileList *l1, FileList *l2);
  338. X
  339. X    DESCRIPTION
  340. X        Comparelists first makes an overall assessment of lists <l1> and
  341. X        <l2>.  If the number of files/directories in the lists differ,
  342. X        that fact is reported.  Next, CompareLists tests for the condition
  343. X        where an entry in one list has the same name as an entry in the
  344. X        other list, but one entry represents a file and the other entry
  345. X        represents a directory.  These entries are removed from the list.
  346. X        CompareFiles is then called to compare all file nodes, removing
  347. X        them as they are "used".  Finally, CompareDirs is called to
  348. X        compare all directory nodes, again removing the nodes as they
  349. X        are used. A non-zero return indicates a fatal error.
  350. X*/
  351. Xint
  352. XCompareLists(FileList *l1, FileList *l2)
  353. X{
  354. Xstatic char *isDirMsg =     " is a directory";
  355. Xstatic char *isFileMsg =    " is a file";
  356. X
  357. X    FileNode    *f1, *f2, *nextEntry;
  358. X    int result = 0;
  359. X
  360. X    ++level;
  361. X    if (verbose) {
  362. X        printf("DiffDir: comparing directory\n '%s' to '%s'\n",
  363. X               l1->dName, l2->dName);
  364. X    }
  365. X    /* Scan the lists for nodes whose names match but whose types
  366. X       differ (file vs. directory).
  367. X    */
  368. X    for (f1 = l1->firstEntry; f1; f1 = nextEntry) {
  369. X        nextEntry = f1->next;
  370. X        f2 = FindFile(f1, l2);
  371. X        if (f2 && (f2->isDir != f1->isDir) ) {  /* Ooops! */
  372. X            printf("*** '%s%s' %s\n*** but '%s%s' %s!\n",
  373. X                   l1->dName,f1->name, f1->isDir ? isDirMsg : isFileMsg,
  374. X                   l2->dName,f2->name, f2->isDir ? isDirMsg : isFileMsg);
  375. X            FreeNode(f1, l1);
  376. X            FreeNode(f2, l2);
  377. X        }
  378. X    }
  379. X    if (! (result = CompareFiles(l1, l2)))
  380. X        result = CompareDirs(l1, l2);
  381. X    --level;
  382. X    return result;
  383. X}
  384. X
  385. X/*  FUNCTION
  386. X        CompareDirs - compare directory entries.
  387. X
  388. X    SYNOPSIS
  389. X        int CompareDirs(FileList *list1, FileList *list2);
  390. X
  391. X    DESCRIPTION
  392. X        CompareDirs scans <list1>, attempting to match its directory node
  393. X        entries with entries in <list2>.  For each matching entry found,
  394. X        CompareDirs creates a new sublist, recursively calling CompareLists
  395. X        to compare the contents of those directories.  When CompareLists
  396. X        returns, CompareDirs removes the "used" directory entries from
  397. X        both lists. A non-zero return code indicates a fatal error.
  398. X*/
  399. X
  400. Xint
  401. XCompareDirs(FileList *list1, FileList *list2)
  402. X{
  403. Xstatic char *missing = "*** Directory missing: '%s%s'\n";
  404. X
  405. X    FileNode *n1, *n2, *nextEntry;
  406. X    int      result = 0;
  407. X    FileList *subList1, *subList2;
  408. X
  409. X    for (n1 = list1->firstEntry; n1 && !result; n1 = nextEntry) {
  410. X        nextEntry = n1->next;
  411. X        /* Note: there should only be directory nodes in the list
  412. X           at this point!
  413. X        */
  414. X        if (! n1->isDir) {
  415. X            puts("DiffDir: non-directory node found in CompareDirs!");
  416. X            MyExit(20);                 /* What else can we do? */
  417. X        }
  418. X        n2 = FindFile(n1, list2);
  419. X        if (n2 == NULL) {
  420. X            printf(missing, list2->dName, n1->name);
  421. X        }
  422. X        else {
  423. X            subList1 = MyAlloc( sizeof(FileList) );
  424. X            subList1->dName = MakeDirName(list1->dName, n1->name);
  425. X            subList2 = MyAlloc( sizeof(FileList) );
  426. X            subList2->dName = MakeDirName(list2->dName, n2->name);
  427. X            result = CollectFiles(subList1);
  428. X            if (!result)
  429. X                result = CollectFiles(subList2);
  430. X            if (!result)
  431. X                result = CompareLists(subList1, subList2);
  432. X
  433. X            /* Give back the memories :-) */
  434. X            MyFree(subList1->dName);
  435. X            MyFree(subList1);
  436. X            MyFree(subList2->dName);
  437. X            MyFree(subList2);
  438. X        }
  439. X        FreeNode(n1, list1);
  440. X        if (n2) FreeNode(n2, list2);
  441. X    }
  442. X    if (!result) {
  443. X        for (n2 = list2->firstEntry; n2; n2 = nextEntry) {
  444. X            nextEntry = n2->next;
  445. X            printf(missing, list1->dName, n2->name);
  446. X            FreeNode(n2, list2);
  447. X        }
  448. X    }
  449. X    return result;
  450. X}
  451. X
  452. X/*  FUNCTION
  453. X        CompareFile - compare the attributes of two similar files.
  454. X
  455. X    SYNOPSIS
  456. X        void CompareFile(FileNode *f1, FileNode *f2);
  457. X
  458. X    DESCRIPTION
  459. X        CompareFile is called with two file description nodes, <f1> and
  460. X        <f2> which are expected to represent similar files in different
  461. X        directory hierarchies.  CompareFile compares the currently selected
  462. X        file attributes and will report any discrepancies to standard output.
  463. X*/
  464. Xvoid
  465. XCompareFile(FileNode *f1, FileNode *f2)
  466. X{
  467. X    USHORT error = 0, item, mask;
  468. X
  469. X    if (f1->isDir != f2->isDir) {
  470. X        puts("*** File type mismatch (file vs. dir) - program error!");
  471. X        FreeNode(f1, f1->parentList);
  472. X        FreeNode(f2, f2->parentList);
  473. X    }
  474. X    else {
  475. X        if ((testFlags & FLAGS_DONT_MATCH) && (f1->flags != f2->flags) )
  476. X            error |= FLAGS_DONT_MATCH;
  477. X
  478. X        if ((testFlags & DATES_DONT_MATCH) && 
  479. X            (CompareDS( (long *) &f1->date, (long *) &f2->date) ) )
  480. X            error |= DATES_DONT_MATCH;
  481. X
  482. X        if (!ignoreCase) {
  483. X            if (strcmp(f1->name, f2->name) != 0)
  484. X                error |= NAMES_DONT_MATCH;
  485. X        }
  486. X
  487. X        if ( (testFlags & SIZES_DONT_MATCH) && (f1->size != f2->size) ) {
  488. X            error |= SIZES_DONT_MATCH;
  489. X        }
  490. X
  491. X        /* Do we want to compare all files? */
  492. X
  493. X        if (scriptFile) {
  494. X            fprintf(scriptFile, "%s %s%s %s%s\n",
  495. X                    scriptCmd,
  496. X                    f1->parentList->dName,f1->name,
  497. X                    f2->parentList->dName,f2->name);
  498. X        }
  499. X
  500. X        if ((testFlags & COMMENTS_DONT_MATCH) &&
  501. X            (strcmp(f1->comment, f2->comment) != 0) )
  502. X            error |= COMMENTS_DONT_MATCH;
  503. X    }
  504. X
  505. X    if (error) {                    /* Aw, darn... */
  506. X        printf("*** Mismatch: ");
  507. X        for (item = 0, mask = 1; item < ITEM_COUNT;
  508. X             ++item, mask= (mask << 1)) {
  509. X            if (error & mask)
  510. X                printf(errorDesc[item]);
  511. X        }
  512. X        puts("");
  513. X        puts(f1->parentList->dName);
  514. X        WriteFileInfo(f1);
  515. X        puts("------------------------------------");
  516. X        puts(f2->parentList->dName);
  517. X        WriteFileInfo(f2);
  518. X        puts("====================================");
  519. X    }
  520. X    if (checkContents && (f1->size == f2->size) ) {
  521. X        CompareFileContents(f1, f2);
  522. X    }
  523. X}
  524. X
  525. X
  526. X/*  FUNCTION
  527. X *      CompareFileContents - do a binary comparison of two files.
  528. X *
  529. X *  SYNOPSIS
  530. X *      void CompareFileContents(FileNode *f1, FileNode *f2);
  531. X *
  532. X *  DESCRIPTION
  533. X *      This function performs a byte-by-byte comparison of two files,
  534. X *      terminating with an error message on the first mismatch.
  535. X */
  536. Xvoid
  537. XCompareFileContents(FileNode *f1, FileNode *f2)
  538. X{
  539. X#define BUFFER_SIZE     32768
  540. X
  541. X    static char *readErr = "Read error on file '%s%s'\n";
  542. X
  543. X    int     fd1 = -1, fd2 = -1;
  544. X    char    *buf1 = NULL, *buf2 = NULL;
  545. X    size_t  i;
  546. X    size_t  leng1, leng2;
  547. X
  548. X    buf1 = MyAlloc(BUFFER_SIZE);
  549. X    buf2 = MyAlloc(BUFFER_SIZE);
  550. X
  551. X    fd1 = OpenFile(f1);
  552. X    if (fd1 == -1) goto done;
  553. X
  554. X    fd2 = OpenFile(f2);
  555. X    if (fd2 == -1) goto done;
  556. X
  557. X    while (1) {
  558. X        leng1 = read(fd1, buf1, BUFFER_SIZE);
  559. X        leng2 = read(fd2, buf2, BUFFER_SIZE);
  560. X        if (leng1 == -1) {
  561. X            printf(readErr,f1->parentList->dName,f1->name);
  562. X            goto done;
  563. X        }
  564. X        if (leng2 == -1) {
  565. X            printf(readErr,f2->parentList->dName, f2->name);
  566. X            goto done;
  567. X        }
  568. X        if (leng1 != leng2) {
  569. X            printf("File length mismatch: '%s%s' vs. '%s%s'\n",
  570. X                    f1->parentList->dName,f1->name,
  571. X                    f2->parentList->dName,f2->name);
  572. X        }
  573. X        for (i = 0; i < leng1; ++i) {
  574. X            if (buf1[i] != buf2[i]) {
  575. X                printf(
  576. X                "*** File data mismatch at byte %d:\n    '%s%s' vs. '%s%s'\n",
  577. X                        i, 
  578. X                        f1->parentList->dName,f1->name,
  579. X                        f2->parentList->dName,f2->name);
  580. X                goto done;
  581. X            }
  582. X        }
  583. X        if (leng1 < BUFFER_SIZE) break;     /* End of file? */
  584. X    }    
  585. X
  586. Xdone:
  587. X    if (buf1) MyFree(buf1);
  588. X    if (buf2) MyFree(buf2);
  589. X    if (fd1 != -1) close(fd1);
  590. X    if (fd2 != -1) close(fd2);    
  591. X}
  592. X
  593. X/*  FUNCTION
  594. X        CompareFiles - compare all file nodes in two lists.
  595. X
  596. X    SYNOPSIS
  597. X        int CompareFiles(FileList *l1, FileList *l2);
  598. X
  599. X    DESCRIPTION
  600. X        The file attributes for all files in list <l1> are compared to
  601. X        those in list <l2>.  Discrepancies are reported to standard
  602. X        output.  After all the files in <l1> have been tested, a second
  603. X        scan is made over list <l2> for any remaining file nodes.  These
  604. X        represent files which were not found in <l1>.  Upon return, all
  605. X        file nodes will have been removed from lists <l1> and <l2>,
  606. X        leaving behind any directory nodes for CompareDirs().
  607. X*/
  608. X
  609. Xint
  610. XCompareFiles(FileList *l1, FileList *l2)
  611. X{
  612. X    static char *missing = "*** File missing: '%s%s'\n";
  613. X    FileNode    *f1, *f2, *nextNode;
  614. X
  615. X    /* Loop through all file entries in list1. Since the list may be
  616. X     * modified along the way, save a look-ahead pointer to the next
  617. X     * entry.
  618. X     */
  619. X
  620. X    for (f1 = l1->firstEntry; f1; f1 = nextNode) {
  621. X        nextNode = f1->next;
  622. X        if (f1->isDir) continue;
  623. X        f2 = FindFile(f1, l2);
  624. X        if (f2 == NULL) {
  625. X            printf(missing, l2->dName, f1->name);
  626. X        }
  627. X        else {
  628. X            CompareFile(f1, f2);
  629. X        }
  630. X        FreeNode(f1, l1);
  631. X        if (f2)
  632. X            FreeNode(f2, l2);
  633. X    }
  634. X
  635. X    /* Look for "leftovers" in list 2. */
  636. X
  637. X    for (f2 = l2->firstEntry; f2; f2 = nextNode) {
  638. X        nextNode = f2->next;
  639. X        if (f2->isDir) continue;
  640. X        printf(missing, l1->dName, f2->name);
  641. X        FreeNode(f2, l2);
  642. X    }
  643. X    return 0;
  644. X}
  645. X
  646. X/*  FUNCTION
  647. X        DupString - duplicate a string.
  648. X
  649. X    SYNOPSIS
  650. X        char *DupString(const char *oldString);
  651. X
  652. X    DESCRIPTION
  653. X        DupString dynamically allocates space for a new copy of <oldString>,
  654. X        copies <oldString> to the new area and returns a pointer to the new
  655. X        string.
  656. X
  657. X*/
  658. X
  659. Xchar *
  660. XDupString(const char *oldString)
  661. X{
  662. X    char *newString;
  663. X
  664. X    newString = MyAlloc(strlen(oldString)+1);
  665. X    strcpy(newString, oldString);
  666. X    return newString;
  667. X}
  668. X
  669. X/*  FUNCTION
  670. X        FindFile - find a file node by name.
  671. X
  672. X    SYNOPSIS
  673. X        FileNode *FindFile(FileNode *node, FileNode *list)
  674. X
  675. X    DESCRIPTION
  676. X        FindFile searches <list> for a file description node whose name
  677. X        matches the name in <node>.  A case-insensitive name comparison
  678. X        is performed.  If the matching entry is found, a pointer to it
  679. X        is returned.  Otherwise, NULL is returned.
  680. X*/
  681. X
  682. XFileNode *
  683. XFindFile(FileNode *node, FileList *list)
  684. X{
  685. X    FileNode *tNode;
  686. X
  687. X    for (tNode = list->firstEntry; tNode; tNode = tNode->next) {
  688. X        if (stricmp(node->name, tNode->name) == 0)
  689. X            return tNode;
  690. X    }
  691. X    return NULL;                    /* Sorry...not found. */
  692. X}
  693. X
  694. X/*  FUNCTION
  695. X        FreeNode - free a file node from a list.
  696. X
  697. X    SYNOPSIS
  698. X        void FreeNode(node, list)
  699. X             FileNode *node;
  700. X             FileList *list;
  701. X*/
  702. Xvoid
  703. XFreeNode(FileNode *node, FileList *list)
  704. X{
  705. X    if (node->prev)
  706. X        node->prev->next = node->next;
  707. X    if (node->next)
  708. X        node->next->prev = node->prev;
  709. X    if (node == list->firstEntry)
  710. X        list->firstEntry = node->next;
  711. X    if (node == list->lastEntry)
  712. X        list->lastEntry = node->prev;
  713. X
  714. X    MyFree(node->name);
  715. X    MyFree(node->comment);
  716. X    MyFree(node);
  717. X}
  718. X
  719. X/*  FUNCTION
  720. X        MakeDirName - assemble a directory name from components.
  721. X
  722. X    SYNOPSIS
  723. X        char *MakeDirName(const char *s1, const char *s2);
  724. X
  725. X    DESCRIPTION
  726. X        MakeDirName dynamically allocates a string large enough to hold
  727. X        a composite name formed from strings <s1> and <s2>. It also adds
  728. X        a directory separator (/) to the end of the new name if the
  729. X        new result does not end in a colon (:).  The new name is returned
  730. X        as the function result.
  731. X*/
  732. Xchar *
  733. XMakeDirName(const char *s1, const char *s2)
  734. X{
  735. X    char    *dirName;
  736. X
  737. X    dirName = MyAlloc(strlen(s1)+strlen(s2)+2);
  738. X    strcpy(dirName, s1);
  739. X    strcat(dirName, s2);
  740. X    if (dirName[strlen(dirName)-1] != ':') strcat(dirName, "/");
  741. X    return dirName;
  742. X}
  743. X
  744. X/*  FUNCTION
  745. X        MyAlloc - perform memory allocation with error checking.
  746. X
  747. X    SYNOPSIS
  748. X        void *MyAlloc(size_t size);
  749. X
  750. X    DESCRIPTION
  751. X        MyAlloc attempts to allocate <size> bytes of memory.  If it fails,
  752. X        an error message is sent to standard output and the program is
  753. X        terminated.  Otherwise, MyAlloc returns a pointer to the newly
  754. X        allocated (zero-filled) memory block.
  755. X*/
  756. Xvoid *
  757. XMyAlloc(size_t size)
  758. X{
  759. X
  760. X    long *ptr;
  761. X
  762. X    size += 4;                      /* Add word for size tracking. */
  763. X    ptr = AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC);
  764. X    if (ptr == NULL) {
  765. X        printf("DiffDir: failed to allocate %ld bytes!\n", size);
  766. X        MyExit(20);
  767. X    }
  768. X    *ptr = size;                    /* Store size. */
  769. X    ++ptr;                          /* Advance pointer to data block. */
  770. X    memInUse += size;
  771. X    if (memInUse > maxMemUsed) maxMemUsed = memInUse;
  772. X    return (void *) ptr;
  773. X}
  774. X
  775. X/*  FUNCTION
  776. X        MyExit - terminate program with cleanup.
  777. X
  778. X    SYNOPSIS
  779. X        void MyExit(int code);
  780. X
  781. X    DESCRIPTION
  782. X        MyExit simply provides a graceful way for the program to exit,
  783. X        performing any necessary cleanup chores.
  784. X*/
  785. Xvoid
  786. XMyExit(int code)
  787. X{
  788. X    if (fib) MyFree(fib);
  789. X    ReportStats();
  790. X    exit(code);
  791. X}
  792. X
  793. X
  794. X/*  FUNCTION
  795. X *      MyFree - free memory block, tracking memory usage.
  796. X *
  797. X *  SYNOPSIS
  798. X *      void MyFree(void *);
  799. X *
  800. X *  DESCRIPTION
  801. X *      MyFree releases a block of memory allocated by MyAlloc. It
  802. X *      also safeguards against null pointers.
  803. X */
  804. X
  805. Xvoid MyFree(void * ptr)
  806. X{
  807. X    long    size;
  808. X    long    *xptr;
  809. X
  810. X    if (ptr) {                      /* Ignore null pointers. */
  811. X        xptr = (long *)ptr;
  812. X        --xptr;
  813. X        size = *xptr;
  814. X        FreeMem(xptr, size);
  815. X        memInUse -= size;
  816. X    }
  817. X}
  818. X
  819. X/*  FUNCTION
  820. X *      OpenFile - open a file for read-only access.
  821. X *
  822. X *  SYNOPSIS
  823. X *      int OpenFile(FileNode *f);
  824. X *
  825. X *  DESCRIPTION
  826. X *      OpenFile opens a file for non-buffered I/O. It returns a file
  827. X *      descriptor which is -1 if the file fails to open
  828. X */
  829. X
  830. Xint
  831. XOpenFile(FileNode *f)
  832. X{
  833. X    int     fd;
  834. X    char    fileName[256];
  835. X
  836. X    strcpy(fileName, f->parentList->dName);
  837. X    strcat(fileName, f->name);
  838. X    fd = open(fileName, O_RDONLY);
  839. X    if (fd == -1) {
  840. X        printf("** Failed to open file: '%s'\n", fileName);
  841. X    }
  842. X    return fd;
  843. X}
  844. X
  845. X/*  FUNCTION
  846. X        ReportStats - report program statistics.
  847. X
  848. X    SYNOPSIS
  849. X        void ReportStats(void);
  850. X
  851. X    DESCRIPTION
  852. X        ReportMem reports the maximum memory used, total number of file
  853. X        nodes and total number of directory nodes for this invocation
  854. X        of DiffDir, ONLY if the verbose option is turned on or if the
  855. X        program terminates abnormally.
  856. X*/
  857. Xvoid
  858. XReportStats(void)
  859. X{
  860. X    printf("DiffDir: Files: %ld; directories: %ld; max memory usage: %ld bytes\n",
  861. X           totalFiles, totalDirs, maxMemUsed);
  862. X}
  863. X
  864. X
  865. X/*  FUNCTION
  866. X        stricmp - perform a case-insensitive string compare.
  867. X
  868. X    SYNOPSIS
  869. X        int stricmp(const char *s1, const char *s2);
  870. X
  871. X    DESCRIPTION
  872. X        Strings <s1> and <s2> are compared, ignoring differences in case.
  873. X        A result code is returned according to the following:
  874. X            0   => strings match
  875. X           <0   => s1 < s2
  876. X           >0   => s1 > s2
  877. X*/
  878. X
  879. Xint
  880. Xstricmp(const char *s1, const char *s2)
  881. X{
  882. X    int c1, c2, cd;
  883. X
  884. X    do {
  885. X        c1 = tolower(*s1++);
  886. X        c2 = tolower(*s2++);
  887. X        if (cd = (c1 - c2)) break;
  888. X    } while (c1 && c2);
  889. X
  890. X    return cd;
  891. X}
  892. X
  893. X/*  FUNCTION
  894. X        Usage - describe program usage and exit.
  895. X
  896. X    SYNOPSIS
  897. X        void Usage(void);
  898. X
  899. X    DESCRIPTION
  900. X        Usage is called when the user invokes DiffDir with incorrect
  901. X        or insufficient parameters.  The correct invocation syntax
  902. X        is displayed and the program is terminated.
  903. X*/
  904. Xvoid
  905. XUsage(void)
  906. X{
  907. X    puts(version);
  908. X    puts(
  909. X"Usage: DiffDir [ options ] dir1 dir2\n"
  910. X"   where options may be:\n"
  911. X"     -nocase               ignore filename case differences\n"
  912. X"     -nocomments           ignore comment (filenote) differences\n"
  913. X"     -nocontents           don't compare file contents when sizes are equal\n"
  914. X"     -nodates              ignore creation/modification date differences\n"
  915. X"     -noflags              ignore flag (protection word) differences\n"
  916. X"     -nosizes              ignore file sizes (dumb, but if you need to...)\n"
  917. X"     -script <scriptfile>  generate a comparison script (name = scriptfile)\n"
  918. X"     -command <string>     use <string> to create script commands\n"
  919. X"     -verbose              generate verbose output\n"
  920. X    "\n");
  921. X
  922. X    MyExit(20);
  923. X}
  924. X
  925. X/*  FUNCTION
  926. X        WriteFileInfo - write a full file description to standard output.
  927. X
  928. X    SYNOPSIS
  929. X        void WriteFileInfo(FileNode *node);
  930. X
  931. X    DESCRIPTION
  932. X        WriteFileInfo writes complete info about the file specified by
  933. X        <node> to the standard output.  This only happens when an error
  934. X        occurs.
  935. X
  936. X*/
  937. X
  938. Xvoid
  939. XWriteFileInfo(FileNode *node)
  940. X{
  941. X    static char flagSetNames[9] = {
  942. X        '-', '-', '-', '-', 'a', 'p', 's', 'h', 'C'
  943. X        };
  944. X    static char flagClearNames[9] = {
  945. X        'd', 'e', 'w', 'r', '-', '-', '-', '-', '-'
  946. X        };
  947. X
  948. X    ULONG   flags;
  949. X    SHORT   i;
  950. X    ULONG   mask;
  951. X    char    temp[30];
  952. X
  953. X    DSToStr(temp,"%02m-%02d-%02y %02h:%02n:%02s ", (long *) &node->date);
  954. X    printf(temp);
  955. X    flags = node->flags;
  956. X    for (i = 0, mask = 1; i < 9; ++i, mask = (mask << 1) )
  957. X        if (flags & mask)
  958. X            temp[8 - i] = flagSetNames[i];
  959. X        else
  960. X            temp[8 - i] = flagClearNames[i];
  961. X
  962. X    temp[9] = '\0';
  963. X
  964. X    printf("%s %8ld %s\n", temp, node->size, node->name);
  965. X    if (node->comment)
  966. X        printf(": %s\n",node->comment);
  967. X}
  968. END_OF_FILE
  969. if test 27265 -ne `wc -c <'DiffDir.c'`; then
  970.     echo shar: \"'DiffDir.c'\" unpacked with wrong size!
  971. fi
  972. # end of 'DiffDir.c'
  973. fi
  974. if test -f 'MRDates.c' -a "${1}" != "-c" ; then 
  975.   echo shar: Will not clobber existing file \"'MRDates.c'\"
  976. else
  977. echo shar: Extracting \"'MRDates.c'\" \(15280 characters\)
  978. sed "s/^X//" >'MRDates.c' <<'END_OF_FILE'
  979. X/*
  980. X    MRDates - AmigaDOS date support routines.
  981. X    07/03/88
  982. X
  983. X    This package is a hybrid of code from Thad Floryan, Doug Merrit and
  984. X    myself.  I wanted a reliable set of AmigaDOS date conversion routines
  985. X    and this combination seems to work pretty well.  The star of the show
  986. X    here is Thad's algorithm for converting the "days elapsed" field of
  987. X    an AmigaDOS DateStamp, using an intermediate Julian date format.  I
  988. X    lifted/embellished some of the data structures from Doug's ShowDate
  989. X    package and wrote the DateToDS function.
  990. X
  991. X    History:    (most recent change first)
  992. X
  993. X    12/31/88 -MRR-
  994. X        StrToDS was not handling the null string properly.
  995. X
  996. X    11/01/88 -MRR- Added Unix-style documentation.
  997. X
  998. X    07/03/88 - Changed some names:
  999. X                  Str2DS => StrToDS
  1000. X                  DS2Str => DSToStr
  1001. X */
  1002. X
  1003. X#define MRDATES
  1004. X#include "MRDates.h"
  1005. X#include <exec/types.h>
  1006. X#include <stdio.h>
  1007. X#include <ctype.h>
  1008. X#include <string.h>
  1009. X
  1010. X
  1011. X#define DATE_SEPARATORS     "/:-."
  1012. X#define MINS_PER_HOUR       60
  1013. X#define SECS_PER_MIN        60
  1014. X#define SECS_PER_HOUR       (SECS_PER_MIN * MINS_PER_HOUR)
  1015. X#define TICS_PER_SEC        50
  1016. X
  1017. X#define YEARS_PER_CENTURY   100
  1018. X
  1019. X
  1020. X/*
  1021. X   definitions to calculate current date
  1022. X */
  1023. X#define FEB             1   /* index of feb. in table (for leap years) */
  1024. X#define DAYS_PER_WEEK   7
  1025. X#define DAYS_PER_YEAR   365
  1026. X#define YEARS_PER_LEAP  4
  1027. X#define START_YEAR      1978
  1028. X#define FIRST_LEAP_YEAR 1980
  1029. X#define LEAP_ADJUST     (FIRST_LEAP_YEAR - START_YEAR)
  1030. X#define LEAP_FEB_DAYS   29
  1031. X#define NORM_FEB_DAYS   28
  1032. X#define IsLeap(N)       (((N) % YEARS_PER_LEAP) ? 0 : 1)
  1033. X
  1034. X
  1035. X/*  FUNCTION
  1036. X        DSToDate - convert a DateStamp to a DATE.
  1037. X
  1038. X    SYNOPSIS
  1039. X        int DSToDate(dateStamp, date)
  1040. X            struct DateStamp *dateStamp;
  1041. X            DATE *date;
  1042. X
  1043. X    DESCRIPTION
  1044. X      Extracts the date components from an AmigaDOS datestamp.
  1045. X      The calculations herein use the following assertions:
  1046. X
  1047. X      146097 = number of days in 400 years per 400 * 365.2425 = 146097.00
  1048. X       36524 = number of days in 100 years per 100 * 365.2425 =  36524.25
  1049. X        1461 = number of days in   4 years per   4 * 365.2425 =   1460.97
  1050. X
  1051. X    AUTHOR
  1052. X      Thad Floryan, 12-NOV-85
  1053. X      Mods by Mark Rinfret, 04-JUL-88
  1054. X
  1055. X    SEE ALSO
  1056. X        Include file MRDates.h.
  1057. X
  1058. X */
  1059. X
  1060. X#define DDELTA 722449   /* days from Jan.1,0000 to Jan.1,1978 */
  1061. X
  1062. Xstatic int mthvec[] =
  1063. X   {-1, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364};
  1064. X
  1065. Xint
  1066. XDSToDate(ds, date)
  1067. X    long *ds; DATE *date;
  1068. X
  1069. X{
  1070. X
  1071. X    long jdate, day0, day1, day2, day3;
  1072. X    long year, month, day, temp;
  1073. X
  1074. X    jdate = ds[0] + DDELTA;      /* adjust internal date to Julian */
  1075. X
  1076. X    year = (jdate / 146097) * 400;
  1077. X    day0  = day1 = jdate %= 146097;
  1078. X    year += (jdate / 36524) * 100;
  1079. X    day2  = day1 %= 36524;
  1080. X    year += (day2 / 1461) * 4;
  1081. X    day3  = day1 %= 1461;
  1082. X    year += day3 / 365;
  1083. X    month = 1 + (day1 %= 365);
  1084. X    day = month % 30;
  1085. X    month /= 30;
  1086. X
  1087. X    if ( ( day3 >= 59 && day0 < 59 ) ||
  1088. X        ( day3 <  59 && (day2 >= 59 || day0 < 59) ) )
  1089. X      ++day1;
  1090. X
  1091. X    if (day1 > mthvec[1 + month]) ++month;
  1092. X    day = day1 - mthvec[month];
  1093. X    date->Dyear = year;
  1094. X    date->Dmonth = month;
  1095. X    date->Dday = day;
  1096. X    date->Dweekday = ds[0] % DAYS_PER_WEEK;
  1097. X
  1098. X    temp = ds[1];               /* get ds_Minute value */
  1099. X    date->Dhour = temp / MINS_PER_HOUR;
  1100. X    date->Dminute = temp % MINS_PER_HOUR;
  1101. X    date->Dsecond = ds[2] / TICS_PER_SEC;
  1102. X    return 0;
  1103. X}
  1104. X
  1105. X/*  FUNCTION
  1106. X        DateToDS(date, dateStamp)
  1107. X
  1108. X    SYNOPSIS
  1109. X        void DateToDS(date, dateStamp)
  1110. X             DATE *date;
  1111. X             struct DateStamp *dateStamp;
  1112. X
  1113. X    DESCRIPTION
  1114. X        DateToDS converts the special DATE format to a DateStamp.
  1115. X */
  1116. X
  1117. XDateToDS(date, ds)
  1118. X    DATE *date; long *ds;
  1119. X{
  1120. X    long daysElapsed, yearsElapsed, leapYears, thisMonth, thisDay, thisYear;
  1121. X    int month;
  1122. X
  1123. X    /* Note the special handling for year < START_YEAR.  In this case,
  1124. X     * the other fields are not even checked - the user just gets the
  1125. X     * "start of time".
  1126. X     */
  1127. X    if ((thisYear = date->Dyear) < START_YEAR) {
  1128. X        ds[0] = ds[1] = ds[2] = 0;
  1129. X        return;
  1130. X    }
  1131. X    if (IsLeap(thisYear))
  1132. X        calendar[FEB].Mdays = LEAP_FEB_DAYS;
  1133. X
  1134. X    thisDay = date->Dday - 1;
  1135. X    thisMonth = date->Dmonth -1;
  1136. X    yearsElapsed = thisYear - START_YEAR;
  1137. X    leapYears = (yearsElapsed + LEAP_ADJUST -1) / YEARS_PER_LEAP;
  1138. X    daysElapsed = (yearsElapsed * DAYS_PER_YEAR) + leapYears;
  1139. X    for (month = 0; month < thisMonth; ++month)
  1140. X        daysElapsed += calendar[month].Mdays;
  1141. X    daysElapsed += thisDay;
  1142. X    calendar[FEB].Mdays = NORM_FEB_DAYS;
  1143. X    ds[0] = daysElapsed;
  1144. X    ds[1] = date->Dhour * MINS_PER_HOUR + date->Dminute;
  1145. X    ds[2] = date->Dsecond * TICS_PER_SEC;
  1146. X}
  1147. X/*  FUNCTION
  1148. X        CompareDS - compare two DateStamp values.
  1149. X
  1150. X    SYNOPSIS
  1151. X        int CompareDS(date1, date2)
  1152. X            struct DateStamp *date1, *date2;
  1153. X
  1154. X    DESCRIPTION
  1155. X        CompareDS performs an ordered comparison between two DateStamp
  1156. X        values, returning the following result codes:
  1157. X
  1158. X            -1 => date1 < date2
  1159. X             0 => date1 == date2
  1160. X             1 => date1 > date2
  1161. X
  1162. X    NOTE:
  1163. X        This routine makes an assumption about the DateStamp structure,
  1164. X        specifically that it can be viewed as an array of 3 long integers
  1165. X        in days, minutes and ticks order.
  1166. X */
  1167. X
  1168. Xint
  1169. XCompareDS(d1, d2)
  1170. X    long *d1, *d2;
  1171. X{
  1172. X    USHORT i;
  1173. X    long compare;
  1174. X
  1175. X    for (i = 0; i < 3; ++i) {
  1176. X        if (compare = (d1[i] - d2[i])) {
  1177. X            if (compare < 0) return -1;
  1178. X            return 1;
  1179. X        }
  1180. X    }
  1181. X    return 0;                       /* dates match */
  1182. X}
  1183. X
  1184. X/*  FUNCTION
  1185. X        DSToStr - convert a DateStamp to a formatted string.
  1186. X
  1187. X    SYNOPSIS
  1188. X        void DSToStr(str,fmt,d)
  1189. X             char *str, *fmt;
  1190. X             struct DateStamp *d;
  1191. X
  1192. X    DESCRIPTION
  1193. X        DSToStr works a little like sprintf.  It converts a DateStamp
  1194. X        to an ascii formatted string.  The formatting style is dependent
  1195. X        upon the contents of the format string, fmt, passed to this
  1196. X        function.
  1197. X
  1198. X        The content of the format string is very similar to that
  1199. X        for printf, with the exception that the following letters
  1200. X        have special significance:
  1201. X            y => year minus 1900
  1202. X            Y => full year value
  1203. X            m => month value as integer
  1204. X            M => month name
  1205. X            d => day of month (1..31)
  1206. X            D => day name ("Monday".."Sunday")
  1207. X            h => hour in twenty-four hour notation
  1208. X            H => hour in twelve hour notation
  1209. X            i => 12 hour indicator for H notation (AM or PM)
  1210. X            I => same as i
  1211. X            n => minutes    (sorry...conflict with m = months)
  1212. X            N => same as n
  1213. X            s => seconds
  1214. X            S => same as s
  1215. X
  1216. X        All other characters are passed through as part of the normal
  1217. X        formatting process.  The following are some examples with
  1218. X        Saturday, July 18, 1987, 13:53 as an input date:
  1219. X
  1220. X            "%y/%m/%d"          => 87/7/18
  1221. X            "%02m/%02d/%2y"     => 07/18/87
  1222. X            "%D, %M %d, %Y"     => Saturday, July 18, 1987
  1223. X            "%02H:%02m i"       => 01:53 PM
  1224. X            "Time now: %h%m"    => Time now: 13:53
  1225. X
  1226. X */
  1227. Xvoid
  1228. XDSToStr(str,fmt,d)
  1229. X    char *str, *fmt; long *d;
  1230. X{
  1231. X    DATE date;
  1232. X    char fc,*fs,*out;
  1233. X    USHORT ivalue;
  1234. X    char new_fmt[256];          /* make it big to be "safe" */
  1235. X    USHORT new_fmt_lng;
  1236. X    char *svalue;
  1237. X
  1238. X    DSToDate(d, &date);         /* convert DateStamp to DATE format */
  1239. X
  1240. X    *str = '\0';                /* insure output is empty */
  1241. X    out = str;
  1242. X    fs = fmt;                   /* make copy of format string pointer */
  1243. X
  1244. X    while (fc = *fs++) {        /* get format characters */
  1245. X        if (fc == '%') {        /* formatting meta-character? */
  1246. X            new_fmt_lng = 0;
  1247. X            new_fmt[new_fmt_lng++] = fc;
  1248. X            /* copy width information */
  1249. X            while (isdigit(fc = *fs++) || fc == '-')
  1250. X                new_fmt[new_fmt_lng++] = fc;
  1251. X
  1252. X            switch (fc) {       /* what are we trying to do? */
  1253. X            case 'y':           /* year - 1980 */
  1254. X                ivalue = date.Dyear % 100;
  1255. Xwrite_int:
  1256. X                new_fmt[new_fmt_lng++] = 'd';
  1257. X                new_fmt[new_fmt_lng] = '\0';
  1258. X                sprintf(out,new_fmt,ivalue);
  1259. X                out = str + strlen(str);
  1260. X                break;
  1261. X            case 'Y':           /* full year value */
  1262. X                ivalue = date.Dyear;
  1263. X                goto write_int;
  1264. X
  1265. X            case 'm':           /* month */
  1266. X                ivalue = date.Dmonth;
  1267. X                goto write_int;
  1268. X
  1269. X            case 'M':           /* month name */
  1270. X                svalue = calendar[date.Dmonth - 1].Mname;
  1271. Xwrite_str:
  1272. X                new_fmt[new_fmt_lng++] = 's';
  1273. X                new_fmt[new_fmt_lng] = '\0';
  1274. X                sprintf(out,new_fmt,svalue);
  1275. X                out = str + strlen(str);
  1276. X                break;
  1277. X
  1278. X            case 'd':           /* day */
  1279. X                ivalue = date.Dday;
  1280. X                goto write_int;
  1281. X
  1282. X            case 'D':           /* day name */
  1283. X                svalue = dayNames[d[0] % DAYS_PER_WEEK];
  1284. X                goto write_str;
  1285. X
  1286. X            case 'h':           /* hour */
  1287. X                ivalue = date.Dhour;
  1288. X                goto write_int;
  1289. X
  1290. X            case 'H':           /* hour in 12 hour notation */
  1291. X                ivalue = date.Dhour;
  1292. X                if (ivalue >= 12) ivalue -= 12;
  1293. X                goto write_int;
  1294. X
  1295. X            case 'i':           /* AM/PM indicator */
  1296. X            case 'I':
  1297. X                if (date.Dhour >= 12)
  1298. X                    svalue = "PM";
  1299. X                else
  1300. X                    svalue = "AM";
  1301. X                goto write_str;
  1302. X
  1303. X            case 'n':           /* minutes */
  1304. X            case 'N':
  1305. X                ivalue = date.Dminute;
  1306. X                goto write_int;
  1307. X
  1308. X            case 's':           /* seconds */
  1309. X            case 'S':
  1310. X                ivalue = date.Dsecond;
  1311. X                goto write_int;
  1312. X
  1313. X            default:
  1314. X                /* We are in deep caca - don't know what to do with this
  1315. X                 * format character.  Copy the raw format string to the
  1316. X                 * output as debugging information.
  1317. X                 */
  1318. X                new_fmt[new_fmt_lng++] = fc;
  1319. X                new_fmt[new_fmt_lng] = '\0';
  1320. X                strcat(out, new_fmt);
  1321. X                out = out + strlen(out);    /* advance string pointer */
  1322. X                break;
  1323. X            }
  1324. X        }
  1325. X        else
  1326. X            *out++ = fc;        /* copy literal character */
  1327. X    }
  1328. X    *out = '\0';                /* terminating null */
  1329. X}
  1330. X
  1331. X/*  FUNCTION
  1332. X        StrToDS - convert a string to a DateStamp.
  1333. X
  1334. X    SYNOPSIS
  1335. X        int StrToDS(string, date)
  1336. X            char *string;
  1337. X            struct DateStamp *date;
  1338. X
  1339. X    DESCRIPTION
  1340. X        StrToDS expects its string argument to contain a date in
  1341. X        MM/DD/YY HH:MM:SS format.  The time portion is optional.
  1342. X        StrToDS will attempt to convert the string to a DateStamp
  1343. X        representation.  If successful, it will return 0.  On
  1344. X        failure, a 1 is returned.
  1345. X
  1346. X */
  1347. X
  1348. Xint
  1349. XStrToDS(str, d)
  1350. X    char *str; long *d;
  1351. X{
  1352. X    register char c;
  1353. X    int count;
  1354. X    int i, item;
  1355. X    DATE date;              /* unpacked DateStamp */
  1356. X    char *s;
  1357. X
  1358. X    int values[3];
  1359. X    int value;
  1360. X
  1361. X    s = str;
  1362. X    for (item = 0; item < 2; ++item) {  /* item = date, then time */
  1363. X        for (i = 0; i < 3; ++i) values[i] = 0;
  1364. X        count = 0;
  1365. X        while (c = *s++) {          /* get date value */
  1366. X            if (c <= ' ')
  1367. X                break;
  1368. X
  1369. X            if (isdigit(c)) {
  1370. X                value = 0;
  1371. X                do {
  1372. X                    value = value*10 + c - '0';
  1373. X                    c = *s++;
  1374. X                } while (isdigit(c));
  1375. X                if (count == 3) {
  1376. X    bad_value:
  1377. X#ifdef DEBUG
  1378. X                    puts("Error in date-time format.\n");
  1379. X                    printf("at %s: values(%d) = %d, %d, %d\n",
  1380. X                        s, count, values[0], values[1], values[2]);
  1381. X#endif
  1382. X                    return 1;
  1383. X                }
  1384. X                values[count++] = value;
  1385. X                if (c <= ' ')
  1386. X                    break;
  1387. X            }
  1388. X            else if (! strchr(DATE_SEPARATORS, c) )
  1389. X                goto bad_value;     /* Illegal character - quit. */
  1390. X        }                           /* end while */
  1391. X        if (item) {                 /* Getting time? */
  1392. X            date.Dhour = values[0];
  1393. X            date.Dminute = values[1];
  1394. X            date.Dsecond = values[2];
  1395. X        }
  1396. X        else {                      /* Getting date? */
  1397. X
  1398. X/* It's OK to have a null date string, but it's not OK to specify only
  1399. X   1 or 2 of the date components.
  1400. X */
  1401. X            if (count && count != 3)
  1402. X                goto bad_value;
  1403. X            date.Dmonth = values[0];
  1404. X            date.Dday = values[1];
  1405. X            date.Dyear = values[2];
  1406. X            if (date.Dyear == 0) {
  1407. X                date.Dyear = START_YEAR;
  1408. X                date.Dday = 1;
  1409. X            }
  1410. X            else {
  1411. X                if (date.Dyear < (START_YEAR - 1900) )
  1412. X                    date.Dyear += 100;
  1413. X                date.Dyear += 1900;
  1414. X            }
  1415. X        }
  1416. X    }                               /* end for */
  1417. X    DateToDS(&date, d);
  1418. X    return 0;
  1419. X}                                   /* StrToDS */
  1420. X
  1421. X
  1422. X#ifdef DEBUG
  1423. X#include "stdio.h"
  1424. Xmain(ac, av)
  1425. X    int ac;
  1426. X    char    **av;
  1427. X{
  1428. X    long    datestamp[3];           /* A little dangerous with Aztec */
  1429. X    long    datestamp2[3];
  1430. X    DATE    date, oldDate;
  1431. X    long    day, lastDay;
  1432. X    int     errors = 0;
  1433. X
  1434. X    /*
  1435. X     * display results from DateStamp() (hours:minutes:seconds)
  1436. X     */
  1437. X    DateStamp(datestamp);
  1438. X
  1439. X    /*
  1440. X     * display results from DSToDate() (e.g. "03-May-88")
  1441. X     */
  1442. X    DSToDate(datestamp, &date);
  1443. X    printf("Current date: %02d-%s-%02d\n",
  1444. X        date.Dday, calendar[ date.Dmonth - 1].Mname,
  1445. X        (date.Dyear % YEARS_PER_CENTURY));
  1446. X
  1447. X    printf("Current time: %02d:%02d:%02d\n",
  1448. X        date.Dhour, date.Dminute, date.Dsecond);
  1449. X
  1450. X    printf("\nDoing sanity check through year 2000...\n\t");
  1451. X    lastDay = (2000L - START_YEAR) * 365L;
  1452. X    lastDay += (2000L - START_YEAR) / YEARS_PER_LEAP;
  1453. X    for (day = 0; day <= lastDay; ++day) {
  1454. X        if (day % 1000 == 0) {
  1455. X            printf(" %ld", day);
  1456. X            fflush(stdout);
  1457. X        }
  1458. X        datestamp[0] = day;
  1459. X        datestamp[1] = MINS_PER_HOUR - 1;
  1460. X        datestamp[2] = TICS_PER_SEC * (SECS_PER_MIN - 1);
  1461. X        DSToDate(datestamp, &date);
  1462. X        if (day && date == oldDate) {
  1463. X            printf("Got same date for days %d, %d: %02d-%s-%02d\n",
  1464. X                    day - 1, day,
  1465. X                    date.Dday,
  1466. X                    calendar[ date.Dmonth - 1 ].Mname,
  1467. X                    (date.Dyear % YEARS_PER_CENTURY));
  1468. X
  1469. X            if (++errors == 10)
  1470. X                exit(1);
  1471. X        }
  1472. X        DateToDS(&date, datestamp2);
  1473. X        if (day != datestamp2[0]) {
  1474. X            printf("\nConversion mismatch at day %ld!\n", day);
  1475. X            printf("\tBad value = %ld", datestamp2[0]);
  1476. X            printf("\tDate: %02d-%s-%02d\n",
  1477. X                    date.Dday,
  1478. X                    calendar[ date.Dmonth  -1 ].Mname,
  1479. X                    (date.Dyear % YEARS_PER_CENTURY));
  1480. X            if (++errors == 10)
  1481. X                exit(1);
  1482. X        }
  1483. X        oldDate = date;
  1484. X    }
  1485. X    printf("\nSanity check passed.\n");
  1486. X} /* main() */
  1487. X#endif
  1488. X
  1489. END_OF_FILE
  1490. if test 15280 -ne `wc -c <'MRDates.c'`; then
  1491.     echo shar: \"'MRDates.c'\" unpacked with wrong size!
  1492. fi
  1493. # end of 'MRDates.c'
  1494. fi
  1495. if test -f 'MRDates.h' -a "${1}" != "-c" ; then 
  1496.   echo shar: Will not clobber existing file \"'MRDates.h'\"
  1497. else
  1498. echo shar: Extracting \"'MRDates.h'\" \(1183 characters\)
  1499. sed "s/^X//" >'MRDates.h' <<'END_OF_FILE'
  1500. X/* MRDates.h - Declarations for types and variables used by MRDates. */
  1501. X
  1502. X#ifndef MRDATES_H
  1503. X#define MRDATES_H 1
  1504. X
  1505. Xtypedef struct {
  1506. X    int    Dyear;        /* year AD (e.g. 1987)    */
  1507. X    int    Dmonth;        /* month of year (0-11)    */
  1508. X    int    Dday;        /* day in month (1-31)    */
  1509. X    int Dhour;        /* 0-23                 */
  1510. X    int Dminute;    /* 0-59                 */
  1511. X    int Dsecond;    /* 0-59                 */
  1512. X    int    Dweekday;    /* day of week (Sun=0)    */
  1513. X} DATE;
  1514. X
  1515. Xtypedef struct {
  1516. X        char    *Mname;
  1517. X        int     Mdays;
  1518. X        } CalEntry;
  1519. X
  1520. X#ifdef MRDATES
  1521. XCalEntry calendar[12] = {
  1522. X        { "Jan", 31 },   { "Feb", 28 },  { "Mar", 31 }, { "Apr", 30 },
  1523. X        { "May", 31 },   { "Jun", 30 },  { "Jul", 31 }, { "Aug", 31 },
  1524. X        { "Sep", 30 },   { "Oct", 31 },  { "Nov", 30 }, { "Dec", 31 }
  1525. X    };
  1526. X#else
  1527. Xextern CalEntry calendar[12];
  1528. X#endif
  1529. X
  1530. X#ifdef MRDATES
  1531. Xchar *dayNames[7] = {
  1532. X    "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
  1533. X    };
  1534. X#else
  1535. Xextern char *dayNames[7];
  1536. X#endif
  1537. X
  1538. X/* mrdates.c */
  1539. Xint DSToDate(long *ds, DATE *date);
  1540. Xint DateToDS(DATE *date, long *ds);
  1541. Xint CompareDS(long *d1, long *d2);
  1542. Xvoid DSToStr(char *str, char *fmt, long *d);
  1543. Xint StrToDS(char *str, long *d);
  1544. X
  1545. X#endif /* MRDATES_H */
  1546. END_OF_FILE
  1547. if test 1183 -ne `wc -c <'MRDates.h'`; then
  1548.     echo shar: \"'MRDates.h'\" unpacked with wrong size!
  1549. fi
  1550. # end of 'MRDates.h'
  1551. fi
  1552. if test ! -d 'dir1' ; then
  1553.     echo shar: Creating directory \"'dir1'\"
  1554.     mkdir 'dir1'
  1555. fi
  1556. if test -f 'dir1/File1' -a "${1}" != "-c" ; then 
  1557.   echo shar: Will not clobber existing file \"'dir1/File1'\"
  1558. else
  1559. echo shar: Extracting \"'dir1/File1'\" \(18 characters\)
  1560. sed "s/^X//" >'dir1/File1' <<'END_OF_FILE'
  1561. XThis is file 1.
  1562. X
  1563. X
  1564. END_OF_FILE
  1565. if test 18 -ne `wc -c <'dir1/File1'`; then
  1566.     echo shar: \"'dir1/File1'\" unpacked with wrong size!
  1567. fi
  1568. # end of 'dir1/File1'
  1569. fi
  1570. if test -f 'dir1/File3' -a "${1}" != "-c" ; then 
  1571.   echo shar: Will not clobber existing file \"'dir1/File3'\"
  1572. else
  1573. echo shar: Extracting \"'dir1/File3'\" \(18 characters\)
  1574. sed "s/^X//" >'dir1/File3' <<'END_OF_FILE'
  1575. XThis is file 3.
  1576. X
  1577. X
  1578. END_OF_FILE
  1579. if test 18 -ne `wc -c <'dir1/File3'`; then
  1580.     echo shar: \"'dir1/File3'\" unpacked with wrong size!
  1581. fi
  1582. # end of 'dir1/File3'
  1583. fi
  1584. if test ! -d 'dir1/Level2' ; then
  1585.     echo shar: Creating directory \"'dir1/Level2'\"
  1586.     mkdir 'dir1/Level2'
  1587. fi
  1588. if test -f 'dir1/Level2/File1' -a "${1}" != "-c" ; then 
  1589.   echo shar: Will not clobber existing file \"'dir1/Level2/File1'\"
  1590. else
  1591. echo shar: Extracting \"'dir1/Level2/File1'\" \(81 characters\)
  1592. sed "s/^X//" >'dir1/Level2/File1' <<'END_OF_FILE'
  1593. XThis is file 1.
  1594. XIt is longer than the equivalent file in the other directory.
  1595. X
  1596. X
  1597. X
  1598. END_OF_FILE
  1599. if test 81 -ne `wc -c <'dir1/Level2/File1'`; then
  1600.     echo shar: \"'dir1/Level2/File1'\" unpacked with wrong size!
  1601. fi
  1602. # end of 'dir1/Level2/File1'
  1603. fi
  1604. if test -f 'dir1/Level2/File3' -a "${1}" != "-c" ; then 
  1605.   echo shar: Will not clobber existing file \"'dir1/Level2/File3'\"
  1606. else
  1607. echo shar: Extracting \"'dir1/Level2/File3'\" \(18 characters\)
  1608. sed "s/^X//" >'dir1/Level2/File3' <<'END_OF_FILE'
  1609. XThis is file 3.
  1610. X
  1611. X
  1612. END_OF_FILE
  1613. if test 18 -ne `wc -c <'dir1/Level2/File3'`; then
  1614.     echo shar: \"'dir1/Level2/File3'\" unpacked with wrong size!
  1615. fi
  1616. # end of 'dir1/Level2/File3'
  1617. fi
  1618. if test ! -d 'dir1/Level2/Level3' ; then
  1619.     echo shar: Creating directory \"'dir1/Level2/Level3'\"
  1620.     mkdir 'dir1/Level2/Level3'
  1621. fi
  1622. if test -f 'dir1/Level2/Level3/File1' -a "${1}" != "-c" ; then 
  1623.   echo shar: Will not clobber existing file \"'dir1/Level2/Level3/File1'\"
  1624. else
  1625. echo shar: Extracting \"'dir1/Level2/Level3/File1'\" \(18 characters\)
  1626. sed "s/^X//" >'dir1/Level2/Level3/File1' <<'END_OF_FILE'
  1627. XThis is file 1.
  1628. X
  1629. X
  1630. END_OF_FILE
  1631. if test 18 -ne `wc -c <'dir1/Level2/Level3/File1'`; then
  1632.     echo shar: \"'dir1/Level2/Level3/File1'\" unpacked with wrong size!
  1633. fi
  1634. # end of 'dir1/Level2/Level3/File1'
  1635. fi
  1636. if test -f 'dir1/Level2/Level3/File2' -a "${1}" != "-c" ; then 
  1637.   echo shar: Will not clobber existing file \"'dir1/Level2/Level3/File2'\"
  1638. else
  1639. echo shar: Extracting \"'dir1/Level2/Level3/File2'\" \(18 characters\)
  1640. sed "s/^X//" >'dir1/Level2/Level3/File2' <<'END_OF_FILE'
  1641. XThis is file 2.
  1642. X
  1643. X
  1644. END_OF_FILE
  1645. if test 18 -ne `wc -c <'dir1/Level2/Level3/File2'`; then
  1646.     echo shar: \"'dir1/Level2/Level3/File2'\" unpacked with wrong size!
  1647. fi
  1648. # end of 'dir1/Level2/Level3/File2'
  1649. fi
  1650. if test -f 'dir1/Level2/Level3/File3' -a "${1}" != "-c" ; then 
  1651.   echo shar: Will not clobber existing file \"'dir1/Level2/Level3/File3'\"
  1652. else
  1653. echo shar: Extracting \"'dir1/Level2/Level3/File3'\" \(18 characters\)
  1654. sed "s/^X//" >'dir1/Level2/Level3/File3' <<'END_OF_FILE'
  1655. XThis is file 3.
  1656. X
  1657. X
  1658. END_OF_FILE
  1659. if test 18 -ne `wc -c <'dir1/Level2/Level3/File3'`; then
  1660.     echo shar: \"'dir1/Level2/Level3/File3'\" unpacked with wrong size!
  1661. fi
  1662. # end of 'dir1/Level2/Level3/File3'
  1663. fi
  1664. if test -f 'dir1/file2' -a "${1}" != "-c" ; then 
  1665.   echo shar: Will not clobber existing file \"'dir1/file2'\"
  1666. else
  1667. echo shar: Extracting \"'dir1/file2'\" \(18 characters\)
  1668. sed "s/^X//" >'dir1/file2' <<'END_OF_FILE'
  1669. XThis is dile 2.
  1670. X
  1671. X
  1672. END_OF_FILE
  1673. if test 18 -ne `wc -c <'dir1/file2'`; then
  1674.     echo shar: \"'dir1/file2'\" unpacked with wrong size!
  1675. fi
  1676. # end of 'dir1/file2'
  1677. fi
  1678. if test ! -d 'dir2' ; then
  1679.     echo shar: Creating directory \"'dir2'\"
  1680.     mkdir 'dir2'
  1681. fi
  1682. if test -f 'dir2/DifferentType' -a "${1}" != "-c" ; then 
  1683.   echo shar: Will not clobber existing file \"'dir2/DifferentType'\"
  1684. else
  1685. echo shar: Extracting \"'dir2/DifferentType'\" \(0 characters\)
  1686. sed "s/^X//" >'dir2/DifferentType' <<'END_OF_FILE'
  1687. END_OF_FILE
  1688. if test 0 -ne `wc -c <'dir2/DifferentType'`; then
  1689.     echo shar: \"'dir2/DifferentType'\" unpacked with wrong size!
  1690. fi
  1691. # end of 'dir2/DifferentType'
  1692. fi
  1693. if test -f 'dir2/File1' -a "${1}" != "-c" ; then 
  1694.   echo shar: Will not clobber existing file \"'dir2/File1'\"
  1695. else
  1696. echo shar: Extracting \"'dir2/File1'\" \(18 characters\)
  1697. sed "s/^X//" >'dir2/File1' <<'END_OF_FILE'
  1698. XThis is file 1.
  1699. X
  1700. X
  1701. END_OF_FILE
  1702. if test 18 -ne `wc -c <'dir2/File1'`; then
  1703.     echo shar: \"'dir2/File1'\" unpacked with wrong size!
  1704. fi
  1705. # end of 'dir2/File1'
  1706. fi
  1707. if test -f 'dir2/File2' -a "${1}" != "-c" ; then 
  1708.   echo shar: Will not clobber existing file \"'dir2/File2'\"
  1709. else
  1710. echo shar: Extracting \"'dir2/File2'\" \(18 characters\)
  1711. sed "s/^X//" >'dir2/File2' <<'END_OF_FILE'
  1712. XThis is file 2.
  1713. X
  1714. X
  1715. END_OF_FILE
  1716. if test 18 -ne `wc -c <'dir2/File2'`; then
  1717.     echo shar: \"'dir2/File2'\" unpacked with wrong size!
  1718. fi
  1719. # end of 'dir2/File2'
  1720. fi
  1721. if test ! -d 'dir2/Level2' ; then
  1722.     echo shar: Creating directory \"'dir2/Level2'\"
  1723.     mkdir 'dir2/Level2'
  1724. fi
  1725. if test -f 'dir2/Level2/File1' -a "${1}" != "-c" ; then 
  1726.   echo shar: Will not clobber existing file \"'dir2/Level2/File1'\"
  1727. else
  1728. echo shar: Extracting \"'dir2/Level2/File1'\" \(18 characters\)
  1729. sed "s/^X//" >'dir2/Level2/File1' <<'END_OF_FILE'
  1730. XThis is file 1.
  1731. X
  1732. X
  1733. END_OF_FILE
  1734. if test 18 -ne `wc -c <'dir2/Level2/File1'`; then
  1735.     echo shar: \"'dir2/Level2/File1'\" unpacked with wrong size!
  1736. fi
  1737. # end of 'dir2/Level2/File1'
  1738. fi
  1739. if test -f 'dir2/Level2/File2' -a "${1}" != "-c" ; then 
  1740.   echo shar: Will not clobber existing file \"'dir2/Level2/File2'\"
  1741. else
  1742. echo shar: Extracting \"'dir2/Level2/File2'\" \(18 characters\)
  1743. sed "s/^X//" >'dir2/Level2/File2' <<'END_OF_FILE'
  1744. XThis is file 2.
  1745. X
  1746. X
  1747. END_OF_FILE
  1748. if test 18 -ne `wc -c <'dir2/Level2/File2'`; then
  1749.     echo shar: \"'dir2/Level2/File2'\" unpacked with wrong size!
  1750. fi
  1751. # end of 'dir2/Level2/File2'
  1752. fi
  1753. if test -f 'dir2/Level2/File3' -a "${1}" != "-c" ; then 
  1754.   echo shar: Will not clobber existing file \"'dir2/Level2/File3'\"
  1755. else
  1756. echo shar: Extracting \"'dir2/Level2/File3'\" \(18 characters\)
  1757. sed "s/^X//" >'dir2/Level2/File3' <<'END_OF_FILE'
  1758. XThis is file 3.
  1759. X
  1760. X
  1761. END_OF_FILE
  1762. if test 18 -ne `wc -c <'dir2/Level2/File3'`; then
  1763.     echo shar: \"'dir2/Level2/File3'\" unpacked with wrong size!
  1764. fi
  1765. # end of 'dir2/Level2/File3'
  1766. fi
  1767. if test ! -d 'dir2/Level2/Level3' ; then
  1768.     echo shar: Creating directory \"'dir2/Level2/Level3'\"
  1769.     mkdir 'dir2/Level2/Level3'
  1770. fi
  1771. if test -f 'dir2/Level2/Level3/File2' -a "${1}" != "-c" ; then 
  1772.   echo shar: Will not clobber existing file \"'dir2/Level2/Level3/File2'\"
  1773. else
  1774. echo shar: Extracting \"'dir2/Level2/Level3/File2'\" \(18 characters\)
  1775. sed "s/^X//" >'dir2/Level2/Level3/File2' <<'END_OF_FILE'
  1776. XThis is file 2.
  1777. X
  1778. X
  1779. END_OF_FILE
  1780. if test 18 -ne `wc -c <'dir2/Level2/Level3/File2'`; then
  1781.     echo shar: \"'dir2/Level2/Level3/File2'\" unpacked with wrong size!
  1782. fi
  1783. # end of 'dir2/Level2/Level3/File2'
  1784. fi
  1785. if test -f 'dir2/Level2/Level3/File3' -a "${1}" != "-c" ; then 
  1786.   echo shar: Will not clobber existing file \"'dir2/Level2/Level3/File3'\"
  1787. else
  1788. echo shar: Extracting \"'dir2/Level2/Level3/File3'\" \(18 characters\)
  1789. sed "s/^X//" >'dir2/Level2/Level3/File3' <<'END_OF_FILE'
  1790. XThis is file 3.
  1791. X
  1792. X
  1793. END_OF_FILE
  1794. if test 18 -ne `wc -c <'dir2/Level2/Level3/File3'`; then
  1795.     echo shar: \"'dir2/Level2/Level3/File3'\" unpacked with wrong size!
  1796. fi
  1797. # end of 'dir2/Level2/Level3/File3'
  1798. fi
  1799. if test -f 'dir2/file3' -a "${1}" != "-c" ; then 
  1800.   echo shar: Will not clobber existing file \"'dir2/file3'\"
  1801. else
  1802. echo shar: Extracting \"'dir2/file3'\" \(18 characters\)
  1803. sed "s/^X//" >'dir2/file3' <<'END_OF_FILE'
  1804. XThis is file 3.
  1805. X
  1806. X
  1807. END_OF_FILE
  1808. if test 18 -ne `wc -c <'dir2/file3'`; then
  1809.     echo shar: \"'dir2/file3'\" unpacked with wrong size!
  1810. fi
  1811. # end of 'dir2/file3'
  1812. fi
  1813. if test -f 'makefile' -a "${1}" != "-c" ; then 
  1814.   echo shar: Will not clobber existing file \"'makefile'\"
  1815. else
  1816. echo shar: Extracting \"'makefile'\" \(573 characters\)
  1817. sed "s/^X//" >'makefile' <<'END_OF_FILE'
  1818. X# Aztec C Makefile for DiffDir program
  1819. X
  1820. X#CFLAGS = -bs
  1821. X#LFLAGS = -w -g
  1822. X
  1823. XCFLAGS= -so
  1824. XLFLAGS= 
  1825. X
  1826. XOBJ = DiffDir.o MRDates.o
  1827. X
  1828. XDiffDir: $(OBJ)
  1829. X    ln $(LFLAGS) -o DiffDir $(OBJ) -lc
  1830. X
  1831. XDiffDir.o: DiffDir.c MRDates.h
  1832. X
  1833. Xclean:
  1834. X    delete (#?.o|#?.dbg)
  1835. X
  1836. XSRC = DiffDir.c MRDates.h MRDates.c Makefile
  1837. XBIN = DiffDir.man DiffDir.n DiffDir Sample.Output
  1838. XMISC= dir1 dir2
  1839. X
  1840. XDiffDir.lzh:    $(SRC) $(BIN) $(MISC)
  1841. X    delete (diffdir.lzh)
  1842. X    lharc -b32 -r -xa a DiffDir $(SRC) dir1/* dir2/*
  1843. X
  1844. Xshar:   DiffDir.lzh
  1845. X    uuencode >DiffDir.lzu DiffDir.lzh DiffDir.lzh
  1846. X    makekit -n SHAR DiffDir.lzu
  1847. X
  1848. END_OF_FILE
  1849. if test 573 -ne `wc -c <'makefile'`; then
  1850.     echo shar: \"'makefile'\" unpacked with wrong size!
  1851. fi
  1852. # end of 'makefile'
  1853. fi
  1854. echo shar: End of archive 1 \(of 1\).
  1855. cp /dev/null ark1isdone
  1856. MISSING=""
  1857. for I in 1 ; do
  1858.     if test ! -f ark${I}isdone ; then
  1859.     MISSING="${MISSING} ${I}"
  1860.     fi
  1861. done
  1862. if test "${MISSING}" = "" ; then
  1863.     echo You have the archive.
  1864.     rm -f ark[1-9]isdone
  1865. else
  1866.     echo You still need to unpack the following archives:
  1867.     echo "        " ${MISSING}
  1868. fi
  1869. ##  End of shell archive.
  1870. exit 0
  1871. -- 
  1872. Mail submissions (sources or binaries) to <amiga@uunet.uu.net>.
  1873. Mail comments to the moderator at <amiga-request@uunet.uu.net>.
  1874. Post requests for sources, and general discussion to comp.sys.amiga.misc.
  1875.